﻿#include "framework.h"
#include "Resource.h"
#include "NesMain.h"
#include <Mmsystem.h>
#include <sys/timeb.h>

#include "WinDebug.h"
#include "DebugWinForm.h"
#include "commdlg.h"
#include "palette.h"
#include <stdio.h>
#pragma comment(lib, "Winmm.lib")
#define MAX_LOADSTRING 100



//BOOL dislog = false;

BOOL recreate = true;
HBITMAP memBmp = nullptr;
HDC memDc = nullptr;
BYTE* pBits = nullptr;
HBITMAP oldBmp = nullptr;


NesMain::NesMain(WinForm* parent) :WinForm(parent)
{
	LoadStringW(mInstance, IDS_APP_TITLE, mTitle, MAX_LOADSTRING);
	LoadStringW(mInstance, IDC_SIMPLENES, mWindowsClass, MAX_LOADSTRING);
	mStyle = WS_OVERLAPPEDWINDOW & (~WS_MAXIMIZEBOX);

	SetMenu(IDC_SIMPLENES);
	SetIcon(IDI_GAME);
	SetClientSize(512 - 32, 480 - 32); //尺寸设置需要在菜单之后，不然不会包含菜单

	debugwf = new DebugWinForm((WinForm*)this);
	famicomThread = new FamicomThread(this);

}
NesMain::~NesMain()
{


}

void NesMain::OnExecNes(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dwl, DWORD dw2)
{
	NesMain* nesmain = ((NesMain*)dwUser);
	if (nesmain->mCurstHide && nesmain->mActivited) {
		nesmain->mCurstHide--;
		if (!nesmain->mCurstHide) {

			nesmain->Send(WM_USER + 1, 0, 0);
		}
	}

	nesmain->famicomThread->Clock();
}

#define SCALE 2

inline void NesMain::StartFamicomThread()
{
	timerId = timeSetEvent(16, 2,
		(LPTIMECALLBACK)OnExecNes, (DWORD)this, TIME_PERIODIC);
	famicomThread->Start();
	//hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FamiconThread, this, 0, NULL);
}


FILE* disassemblyfile = nullptr;
int dcount = 0;

void NesMain::Disassembly(char* line) {
	//if (dislog) {
	//	fwrite(line, 1, strlen(line), disassemblyfile);
	//}
}

void NesMain::OnCreate()
{


	//反汇编文件
	//if (fopen_s(&disassemblyfile, "./disassembly.txt", "w+") != 0) {
		//assert(!"nestest 打开失败");
	//}


	//填充BITMAP
	//填充BITMAP信息
	pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pbmi.bmiHeader.biWidth = 256 * SCALE;
	pbmi.bmiHeader.biHeight = -240 * SCALE;
	pbmi.bmiHeader.biPlanes = 1;
	pbmi.bmiHeader.biBitCount = 24;
	pbmi.bmiHeader.biCompression = BI_RGB;

	StartFamicomThread();
}



void NesMain::OnPaint(HDC hdc)
{

	RECT rc;
	UINT8* pMem = mFrameMem;

	famicomThread->WaitSyncLock();
	memcpy(pMem, famicomThread->GetFrameBuffer(), sizeof(mFrameMem));
	famicomThread->ReleaseSyncLock();

	GetClientRect(mWnd, &rc);
	int nWidth = rc.right - rc.left;
	int nHeight = rc.bottom - rc.top;
	if (recreate)
	{
		if (oldBmp != nullptr) {
			SelectObject(memDc, oldBmp);
			oldBmp = nullptr;
		}
		if (memBmp != nullptr) {
			DeleteObject(memBmp);
			memBmp = nullptr;
		}
		if (memDc != nullptr) {
			DeleteObject(memDc);
			memDc = nullptr;
		}

		memDc = CreateCompatibleDC(hdc);
		if (!(memBmp = CreateDIBSection(memDc, &pbmi, DIB_RGB_COLORS, (void**)&pBits, 0, 0)))
		{
			return;
		}
		oldBmp = (HBITMAP)SelectObject(memDc, memBmp);
		recreate = false;
	}


	int offset;
	int x, y, x1, y1;
	UINT8 paletteIndex;

	for (y = 0; y < 240; y++)
	{
		for (x = 0; x < 256; x++)
		{
			//paletteIndex = *pMem;
			PaletteData p = stdpalette[*pMem++];
			for (y1 = 0; y1 < SCALE; y1++) {
				for (x1 = 0; x1 < SCALE; x1++) {
					offset = (y * SCALE + y1) * (256 * 3 * SCALE) + (x * SCALE + x1) * 3;
					pBits[offset + 2] = p.r;
					pBits[offset + 1] = p.g;
					pBits[offset] = p.b;
				}
			}
			//pMem++;
		}
	}

	SetStretchBltMode(hdc, HALFTONE);
	StretchBlt(
		hdc,
		rc.left, rc.top, nWidth, nHeight,
		memDc,
		8 * SCALE, 8 * SCALE, (256 - 16) * SCALE, (240 - 16) * SCALE,
		SRCCOPY);
}

void  NesMain::OnMouseMove(int x, int y)
{
	if (!mCurstHide)
	{
		::ShowCursor(true);
	}
	mCurstHide = 360;
}

bool NesMain::OnKeyUp(unsigned int keyCode)
{
	int i;
	for (i = 0; i < 16; i++)
	{
		if (keyCode == mKeyMap[i])
		{
			famicomThread->Input(i, 0);
			return false;
		}
	}
	if (keyCode == '1')
	{
		famicomThread->WaitSyncLock();
		famicomThread->Save();
		famicomThread->ReleaseSyncLock();
	}
	else if (keyCode == '2')
	{
		famicomThread->WaitSyncLock();
		famicomThread->Restore();
		famicomThread->ReleaseSyncLock();
	}
	return true;
}

bool NesMain::OnKeyDown(unsigned int keyCode)
{
	int i;
	for (i = 0; i < 16; i++)
	{
		if (keyCode == mKeyMap[i])
		{
			famicomThread->Input(i, 1);
			return false;
		}
	}
	return true;
}

void NesMain::OnDestroy()
{
	//fflush(disassemblyfile);
	//fclose(disassemblyfile);
	famicomThread->TryStop();
	famicomThread->Wait();
	delete debugwf;
	delete famicomThread;
	timeKillEvent(timerId);

	if (oldBmp != nullptr)
		SelectObject(memDc, oldBmp);
	if (memBmp != nullptr)
		DeleteObject(memBmp);
	if (memDc != nullptr)
		DeleteObject(memDc);
}

LRESULT NesMain::Dispatch(UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_COMMAND:
		return this->OnCommand(wParam, lParam);
	case WM_ACTIVATE:
		if (famicomThread != nullptr)
		{
			//判断当前窗口时候失去激活
			if (LOWORD(wParam) == WA_INACTIVE)
			{
				mActivited = false;
				famicomThread->Pause();
				//::ShowCursor(false);
			}
			else {
				mActivited = true;
				famicomThread->Play();
			}
		}
		break;
	default:
		if (message == WM_USER + 1)
		{
			::ShowCursor(false);
		}
		break;
	}


	return WinForm::Dispatch(message, wParam, lParam);
}

bool NesMain::OnCommand(WPARAM wParam, LPARAM lParam)
{
	int wmId = LOWORD(wParam);
	switch (wmId)
	{
	case ID_OPENFILE: {

		OPENFILENAME opfn;
		WCHAR strFilename[MAX_PATH];//存放文件名  
		//初始化  
		ZeroMemory(&opfn, sizeof(OPENFILENAME));
		opfn.lStructSize = sizeof(OPENFILENAME);//结构体大小  
		//设置过滤  
		opfn.lpstrFilter = L"nes文件\0*.nes";
		//默认过滤器索引设为1  
		opfn.nFilterIndex = 1;
		//文件名的字段必须先把第一个字符设为 \0  
		opfn.lpstrFile = strFilename;
		opfn.lpstrFile[0] = '\0';
		opfn.nMaxFile = sizeof(strFilename);
		//设置标志位，检查目录或文件是否存在  
		opfn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
		if (GetOpenFileName(&opfn))
		{
			famicomThread->WaitSyncLock();
			int ret;
			if ((ret = famicomThread->LoadRom(strFilename)) != 0)
			{
				WCHAR* buffer[512];
				wsprintf((LPWSTR)buffer, _T("加载ROM出错，返回值%d"), ret);

				::MessageBox(mWnd, (LPWSTR)buffer, _T("ROM加载出错"), MB_OK);
			}
			famicomThread->ReleaseSyncLock();
		}
		break;
	}
	case ID_DEBUG_DISASSEMBLY: {

		debugwf->Show();
		break;
	}
	default:
		return true;
		break;
	}
	return false;
}
